﻿using System;
using System.IO.Ports;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Serialization;
using EpiNEXT.Services;
using EpiNEXT.Services.Collections;

namespace EpiNEXT.Data.P2PSync
{
    [Serializable()]
    public sealed class SmsSyncDriver : SyncDriverBase
    {
        #region Members
        private AutoResetEvent _receiveNow;
        static AutoResetEvent _readNow = new AutoResetEvent(false);
        #endregion // Members

        #region Properties
        [XmlIgnore]
        private SerialPort SerialPort { get; set; }

        [XmlElement]
        public string CarrierConnectionCommand { get; set; }

        [XmlElement]
        public string ComPort { get; set; }

        [XmlElement]
        public int BaudRate { get; set; }
        #endregion // Properties

        public SmsSyncDriver()
            : base()
        {
            CanTransmit = true;
            CanReceive = true;

            SerialPort = new SerialPort();
            SerialPort.PortName = ComPort;
            SerialPort.BaudRate = BaudRate;
            SerialPort.Parity = Parity.None;
            SerialPort.DataBits = 8;
            SerialPort.StopBits = StopBits.One;
            SerialPort.Handshake = Handshake.RequestToSend;
            SerialPort.DtrEnable = true;
            SerialPort.RtsEnable = true;
            SerialPort.NewLine = '\r'.ToString();
            SerialPort.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            SerialPort.ReadTimeout = 300;
            SerialPort.WriteTimeout = 300;
            SerialPort.Encoding = Encoding.GetEncoding("iso-8859-1");

            CarrierConnectionCommand = CarrierConnectionCommand;
        }

        public SmsSyncDriver(string comPort, int baudRate, string connectionCommand = "0,1,\"AT&T\"") 
            : base()
        {
            CanTransmit = true;
            CanReceive = true;

            SerialPort = new SerialPort();
            SerialPort.PortName = comPort;
            SerialPort.BaudRate = baudRate;
            SerialPort.Parity = Parity.None;
            SerialPort.DataBits = 8;
            SerialPort.StopBits = StopBits.One;
            SerialPort.Handshake = Handshake.RequestToSend;
            SerialPort.DtrEnable = true;
            SerialPort.RtsEnable = true;
            SerialPort.NewLine = '\r'.ToString();
            SerialPort.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            SerialPort.ReadTimeout = 300;
            SerialPort.WriteTimeout = 300;
            SerialPort.Encoding = Encoding.GetEncoding("iso-8859-1");

            CarrierConnectionCommand = connectionCommand;
        }

        public void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (e.EventType == SerialData.Chars)
                {
                    _receiveNow.Set();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public override void OpenConnection()
        {
            _receiveNow = new AutoResetEvent(false);
            try
            {
                SerialPort.Open();

                string recievedData = ExecCommand("AT", 300, "No phone connected at ");
                recievedData = ExecCommand("AT+COPS=" + CarrierConnectionCommand, 15000, "Failed to connect to carrier.");
                recievedData = ExecCommand("AT+CMEE=2", 300, "Failed to change format");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public override void CloseConnection()
        {
            try
            {
                SerialPort.Close();
                SerialPort.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
                SerialPort.Dispose();
                SerialPort = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public string ExecCommand(string command, int responseTimeout, string errorMessage)
        {
            try
            {
                SerialPort.DiscardOutBuffer();
                SerialPort.DiscardInBuffer();
                _receiveNow.Reset();
                SerialPort.WriteLine(command);

                string input = ReadResponse(responseTimeout);
                if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
                    throw new ApplicationException("No success message was received.");
                return input;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public string ReadResponse(int timeout)
        {
            string buffer = string.Empty;
            try
            {
                do
                {
                    if (_receiveNow.WaitOne(timeout, false))
                    {
                        string t = SerialPort.ReadExisting();
                        buffer += t;
                    }
                    else
                    {
                        if (buffer.Length > 0)
                            throw new ApplicationException("Response received is incomplete.");
                        else
                            throw new ApplicationException("No data received from phone.");
                    }
                }
                while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return buffer;
        }

        public int CountMessages()
        {
            int CountTotalMessages = 0;
            try
            {

                #region Execute Command

                string recievedData = ExecCommand("AT", 300, "No phone connected at ");
                recievedData = ExecCommand("AT+CMGF=1", 300, "Failed to set message format.");
                String command = "AT+CPMS?";
                recievedData = ExecCommand(command, 1000, "Failed to count SMS message");
                int uReceivedDataLength = recievedData.Length;

                #endregion

                #region If command is executed successfully
                if ((recievedData.Length >= 45) && (recievedData.StartsWith("AT+CPMS?")))
                {

                    #region Parsing SMS
                    string[] strSplit = recievedData.Split(',');
                    string strMessageStorageArea1 = strSplit[0];     //SM
                    string strMessageExist1 = strSplit[1];           //Msgs exist in SM
                    #endregion

                    #region Count Total Number of SMS In SIM
                    CountTotalMessages = Convert.ToInt32(strMessageExist1);
                    #endregion

                }
                #endregion

                #region If command is not executed successfully
                else if (recievedData.Contains("ERROR"))
                {

                    #region Error in Counting total number of SMS
                    string recievedError = recievedData;
                    recievedError = recievedError.Trim();
                    recievedData = "Following error occured while counting the message" + recievedError;
                    #endregion

                }
                #endregion

                return CountTotalMessages;

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public ShortMessageCollection ReadMessage(string command)
        {
            // Set up the phone and read the messages
            ShortMessageCollection messages = null;

            if (!CanReceive) { return messages; }

            try
            {

                #region Execute Command
                // Check connection
                ExecCommand( "AT", 300, "No phone connected");
                // Use message format "Text mode"
                ExecCommand( "AT+CMGF=1", 300, "Failed to set message format.");
                // Use character set "PCCP437"
                ExecCommand( "AT+CSCS=\"PCCP437\"", 300, "Failed to set character set.");
                // Select SIM storage
                ExecCommand( "AT+CPMS=\"SM\"", 300, "Failed to select message storage.");
                // Read the messages
                string input = ExecCommand(command, 5000, "Failed to read the messages.");
                #endregion

                #region Parse messages
                messages = ParseMessages(input);
                #endregion

            }
            catch (Exception ex)
            {
                throw ex;
            }

            if (messages != null)
                return messages;
            else
                return null;
        }

        public bool SendMessage(string phoneNo, int phoneNumberFormat, string message)
        {
            if (!CanTransmit) { return false; }

            bool isSend = false;

            try
            {
                string recievedData = ExecCommand("AT", 300, "No phone or modem is connected");
                recievedData = ExecCommand("AT+CMGF=1", 300, "Failed to set message format.");

                String command = "AT+CMGS=\"" + phoneNo + "\", " + phoneNumberFormat + "";
                recievedData = ExecCommand(command, 500, "Failed to accept phone number.");

                command = message + char.ConvertFromUtf32(26);// '\x001a';//char.ConvertFromUtf32(26); // "\r";
                recievedData = ExecCommand(command, 5000, "Failed to send message"); //3 seconds

                if (recievedData.EndsWith("\r\nOK\r\n"))
                {
                    isSend = true;
                }
                else if (recievedData.Contains("ERROR"))
                {
                    isSend = false;
                }
                return isSend;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }   

        public ShortMessageCollection ParseMessages(string input)
        {
            ShortMessageCollection messages = new ShortMessageCollection();
            try
            {
                Regex r = new Regex(@"\+CMGL: (\d+),""(.+)"",""(.+)"",(.*),""(.+)""\r\n(.+)\r\n");
                Match m = r.Match(input);
                while (m.Success)
                {
                    ShortMessage msg = new ShortMessage();
                    //msg.Index = int.Parse(m.Groups[1].Value);
                    msg.Index = m.Groups[1].Value;
                    msg.Status = m.Groups[2].Value;
                    msg.Sender = m.Groups[3].Value;
                    msg.Alphabet = m.Groups[4].Value;
                    msg.Sent = m.Groups[5].Value;
                    msg.Message = m.Groups[6].Value;
                    messages.Add(msg);

                    m = m.NextMatch();
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }
            return messages;
        }

        static void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                if (e.EventType == SerialData.Chars)
                    _readNow.Set();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public bool DeleteMessage(string command)
        {
            bool isDeleted = false;
            try
            {

                #region Execute Command
                string recievedData = ExecCommand("AT", 300, "No phone connected");
                recievedData = ExecCommand("AT+CMGF=1", 300, "Failed to set message format.");
                recievedData = ExecCommand(command, 300, "Failed to delete message");
                #endregion

                if (recievedData.EndsWith("\r\nOK\r\n"))
                {
                    isDeleted = true;
                }
                if (recievedData.Contains("ERROR"))
                {
                    isDeleted = false;
                }
                return isDeleted;
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }  
    }
}
